package cn.icanci.rec.engine.script.impl;

import cn.icanci.rec.common.enums.ScriptTypeEnum;
import cn.icanci.rec.engine.script.RecScriptEngine;
import cn.icanci.rec.engine.script.context.RecScriptEngineContext;
import cn.icanci.rec.engine.script.factory.ScriptEngineFactory;

import javax.script.*;

/**
 * @author icanci
 * @since 1.0 Created in 2022/11/12 22:46
 */
public class RecScriptEngineImpl implements RecScriptEngine {

    /**
     * 执行脚本
     *
     * @param scriptType 脚本类型
     * @param script 脚本内容
     * @return 脚本执行返回上下文
     */
    @Override
    public RecScriptEngineContext<Object> eval(ScriptTypeEnum scriptType, String script) {
        return eval(scriptType, new SimpleBindings(), script);
    }

    /**
     * 执行脚本
     *
     * @param scriptType 脚本类型
     * @param bindings 脚本运行时参数
     * @param script 脚本内容
     * @return 脚本执行返回上下文
     */
    @Override
    public RecScriptEngineContext<Object> eval(ScriptTypeEnum scriptType, Bindings bindings, String script) {
        RecScriptEngineContext<Object> context = new RecScriptEngineContext<>();
        context.setScriptType(scriptType);
        context.setScriptContent(script);
        context.setType(Object.class);
        context.setBindings(bindings);
        try {
            ScriptEngine scriptEngine = ScriptEngineFactory.getScriptEngine(scriptType);
            context.setScriptEngine(scriptEngine);
            if (scriptEngine instanceof Compilable) {
                Compilable compilable = (Compilable) scriptEngine;
                context.setCompilable(compilable);
                CompiledScript compile = compilable.compile(script);
                context.setCompiledScript(compile);
                Object eval = compile.eval(bindings);
                context.setRetVal(eval);
            } else {
                Object eval = scriptEngine.eval(script, bindings);
                context.setRetVal(eval);
            }
        } catch (Throwable t) {
            context.setThrowable(t);
        }
        return context;
    }

    /**
     * 执行脚本
     *
     * @param scriptType 脚本类型
     * @param script 脚本内容
     * @param clazz 脚本执行返回类型
     * @param <T> 泛型
     * @return 脚本执行返回上下文
     */
    @Override
    public <T> RecScriptEngineContext<T> eval(ScriptTypeEnum scriptType, String script, Class<T> clazz) {
        return eval(scriptType, new SimpleBindings(), script, clazz);
    }

    /**
     * 执行脚本
     *
     * @param scriptType 脚本类型
     * @param bindings 脚本运行时参数
     * @param script 脚本内容
     * @param clazz 脚本执行返回类型
     * @param <T> 泛型
     * @return 脚本执行返回上下文
     */
    @Override
    public <T> RecScriptEngineContext<T> eval(ScriptTypeEnum scriptType, Bindings bindings, String script, Class<T> clazz) {
        RecScriptEngineContext<T> context = new RecScriptEngineContext<>();
        context.setScriptType(scriptType);
        context.setScriptContent(script);
        context.setType(clazz);
        context.setBindings(bindings);
        try {
            ScriptEngine scriptEngine = ScriptEngineFactory.getScriptEngine(scriptType);
            context.setScriptEngine(scriptEngine);
            if (scriptEngine instanceof Compilable) {
                Compilable compilable = (Compilable) scriptEngine;
                context.setCompilable(compilable);
                CompiledScript compile = compilable.compile(script);
                context.setCompiledScript(compile);
                Object eval = compile.eval(bindings);
                context.setRetVal((T) eval);
            } else {
                Object eval = scriptEngine.eval(script, bindings);
                context.setRetVal((T) eval);
            }
        } catch (Throwable t) {
            context.setThrowable(t);
        }
        return context;
    }

}
